/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ruteador.control.geneticAlgorithms;

import java.util.ArrayList;
import java.util.Random;

/**
 *
 * @author jorgeorm
 */
public class IslaTSP extends Thread{
    private Random generador;
    private int numPuntos, generacionActual, tamanoMathingPool,idPuntoInicial, pobMaxima, numMaxGeneraciones, numMaxExtinsiones;
    private double probMutacion, probReproduccion, probSustiuirMalos, probExtinsionMalos, probReproBuenos;
    private ArrayList<CromosomaTSP> poblacion;
    private ArrayList<IslaTSP> islasVecinas;
    private int [][] matrizCostos;
    private boolean termine = false;
    
    public IslaTSP(Random generadorAleatorios, int numeroPuntos, int puntoInicial,int pobMax, int numGeneraciones, 
            double probMuta, double probSustiMalos, double probExtMalos, 
            double probReproBuenos, int maxExtinsion, int [][] matrizCostosNodos){
        this.generador = generadorAleatorios;
        this.generacionActual = 0;
        this.numMaxExtinsiones = maxExtinsion;
        this.numMaxGeneraciones = numGeneraciones;
        this.pobMaxima = pobMax;
        this.probExtinsionMalos = probExtMalos;
        this.probMutacion = probMuta;
        this.probReproduccion = 1.0 - probMuta;
        this.probReproBuenos = probReproBuenos;
        this.probSustiuirMalos = probSustiMalos;
        this.matrizCostos = matrizCostosNodos;
        this.idPuntoInicial = puntoInicial;
        this.tamanoMathingPool = (int) Math.round((double)pobMax * 0.7);
        this.numPuntos = numeroPuntos;
        /*Inicializo la población inicial
         */
    }
    
    public void setTamMathingPool(int tamano){
        tamanoMathingPool = tamano;
    }
    
    public boolean getTermine(){
        return termine;
    }
    
    private void initPoblacionInicial(int tamanoInicial){
        /*TODO: Crear poblacion Inicial
         */
        CromosomaTSP cromosomaTMP = null;
        this.poblacion = new ArrayList(pobMaxima);
        for(int i = 0; i < tamanoInicial; i++){
            cromosomaTMP = new CromosomaTSP(numPuntos, generador, idPuntoInicial);
        }
        
        evaluarHijos(poblacion);
    }
    
    private int generarAleatorioDiferente(int num, int cota){
        int respuesta;
        
        do{
            respuesta = generador.nextInt(cota);
        }while(respuesta != num);
        
        return respuesta;
    }
    
    private ArrayList<CromosomaTSP> seleccionar(){
        ArrayList<CromosomaTSP> mathinPool = new ArrayList(tamanoMathingPool);
        int tamPoblacionActual = poblacion.size();
        int numAleat1, numAleat2;
        CromosomaTSP cromo1, cromo2;
        
        for(int i=0; i < tamanoMathingPool; i++){
            numAleat1 = generador.nextInt(tamPoblacionActual);
            numAleat2 = generarAleatorioDiferente(numAleat1, tamPoblacionActual);
            
            cromo1 = poblacion.get(numAleat1);
            cromo2 = poblacion.get(numAleat2);
            
            if(cromo1.getAptitud() > cromo2.getAptitud()){
                mathinPool.add(cromo1);
            }if(cromo2.getAptitud() > cromo1.getAptitud()){
                mathinPool.add(cromo2);
            }else {
                if(generador.nextDouble()>0.5){
                    mathinPool.add(cromo1);
                }else {
                    mathinPool.add(cromo2);
                }
            }
        }
        
        return mathinPool;
    }
    
    private ArrayList<CromosomaTSP> reproducir(ArrayList<CromosomaTSP> mathingPool){
        ArrayList<CromosomaTSP> hijos;
        
        int numeroAproxHijos = (int) Math.round((double)pobMaxima * 1.5);
        hijos = new ArrayList(numeroAproxHijos);
        
        
        /*TODO:Reproducir el mathing Pool
         */
        int cotaInfSeg,cotaSupSeg,codonI,amplitudReg,posFin,nCromoOp, maxIdInf;
        double randOp;
        
        CromosomaTSP cromoOp;
        cotaInfSeg=(int)Math.round((double)numPuntos*0.4);
        maxIdInf = numPuntos - cotaInfSeg;
        cotaSupSeg=(int)Math.round((double)numPuntos*0.8);
        
        for(int i = 0; i < tamanoMathingPool ; i++){
            randOp = generador.nextDouble();
            
            cromoOp= mathingPool.get(i);
            
            
            /*Tengo preferencia deacuerdo a lo configurado
             */
            if( randOp < probReproduccion){
                /*Reproducir
                 */
                nCromoOp = generarAleatorioDiferente(i, tamanoMathingPool);
                //Punto de inicio y amplitud que se conservaran.
                //Verificar que se conserve entre el 80% y el 40% del cromosoma
                do{
                    codonI = generador.nextInt(maxIdInf);
                } while(codonI == 0);
                
                do{
                    amplitudReg = generador.nextInt(cotaSupSeg);
                    posFin = codonI+amplitudReg;
                }while (cotaInfSeg > amplitudReg || posFin >= numPuntos);
                
                hijos.addAll(cromoOp.reproducirCon(mathingPool.get(nCromoOp), codonI, posFin));
            }else {
                /*Muto
                 */
                int gen1;
                do{
                    gen1 = generador.nextInt(numPuntos);
                }while(gen1 == 0);
                int gen2;
                do{
                    gen2 = generarAleatorioDiferente(gen1, numPuntos);
                }while(gen2 == numPuntos);
                
                hijos.add(cromoOp.mutar(gen1, gen2));
            }
            
        }
        
        return hijos;
    }
    
    private void evaluarHijos(ArrayList<CromosomaTSP> hijos){
        
    }
    
    public void insertarEnPoblacion(ArrayList<CromosomaTSP> poblacion){
        
    }
    
    public ArrayList<CromosomaTSP> getTopCromosomas(int numero){
        ArrayList<CromosomaTSP> top = null;
        /*TODO: definir el método que escoge los mejores cromosomas
         */
        return top;
    }
    
    private void migrar(){
        /*TODO: Implementar la migración con el arrayList de vecinas
         */
    }

    /*TODO: Para la migración
     */
//    public void run (ArrayList<IslaTSP> vecinas){
//        islasVecinas = vecinas;
//        this.start();
//    }
    private synchronized void finaliceIsla(){
        notifyAll();
    }
    
    @Override
    public void run() {
        
        /*Genero la población inicial
         */
        
        initPoblacionInicial(120);
        
        while(generacionActual < numMaxGeneraciones){
            
            /*TODO: Hacer iteraciones evolutivas de la isla
             */
            
            ArrayList<CromosomaTSP> mathingPool = seleccionar();
            
            ArrayList<CromosomaTSP> hijos = reproducir(mathingPool);
            
            evaluarHijos(hijos);
            
            insertarEnPoblacion(hijos);
            
            /*TODO: Atender la migración
             */
//            migrar();
            
            /*Aumento el contador de generaciones al final de todo lo hecho
             */
            generacionActual++;
            
            try{
                 sleep(generador.nextInt(1000));
            }catch( InterruptedException excp){
                System.out.println("Excepción con la corrida del hilo");
            }
        }
        finaliceIsla();
    }
}
